import { app } from "../../base";
import { G_Data } from "../DataCenter";

type RecoverConfig = {
    /**
     * 恢复最小粒度的毫秒数
     */
    cd: number,
    /**
     * 起始值
     */
    from: number,
    /**
     * 目标值
     */
    to: number,
    /**
     * 完整CD内恢复的数据
     */
    unit: number,
}
/**
 * 恢复组件
 * 体力自动恢复、关卡倒计时、HP自动恢复等
 */
export class Recover {
    private _onUpdateListener: Map<(value: number, addValue: number) => void, any>;
    private _config: RecoverConfig;
    private _name: string;
    private _offlineAble: boolean = false;

    private _nextRecoverTime: number = 0;
    private _running: boolean = false;
    private _cancel: boolean = false;
    private _value: number = 0;

    constructor(name?: string, offlineAble: boolean = false) {
        this._name = name;
        this._offlineAble = offlineAble;
    }

    public get isRunning(): boolean {
        return this._running;
    }

    public get value(): number {
        return this._value;
    }

    /**
     * 下一次恢复需要的时间(毫秒)
     */
    public get timeForNextRecover(): number {
        return this._nextRecoverTime > G_Data.server.time ? this._nextRecoverTime - G_Data.server.time : 0;
    }

    /**
     * 修改当前值,通过其他途径获得的,如血瓶\宝箱等
     * @param adjustValue 
     */
    public adjustValue(adjustValue: number): void {
        this._value += adjustValue;
        this._onUpdateListener && this._onUpdateListener.forEach((caller: any, call) => {
            call.apply(caller, [this._value, adjustValue, this._name]);
        });

        if (this.inRange) {
            if (!this._cancel) {
                this.resume()
            }
        } else {
            this.pause();
        }

        if (!this._nextRecoverTime && this.inRange) {
            this._nextRecoverTime = G_Data.server.time + this._config.cd;
            this._offlineAble && app.storage.setUValue(this._name, this._nextRecoverTime);
        }

        this._registerNextRecover();
    }

    /**
     * 开始
     * @param current 当前
     * @param from 恢复范围的起始值
     * @param to 恢复范围的目标值
     * @param cd 恢复CD，毫秒
     * @param unit 单次恢复的量，默认为1
     * @returns 
     * 
     * @example 
     * //关卡倒计时（进入关卡时开始从120倒数，到0后停止，允许玩家通过观看视频增加时间，只要时间小于Number.MAX_SAFE_INTEGER会持续减少。）
     * start(120, Number.MAX_SAFE_INTEGER, 0, 1000, -1)
     * //体力恢复（在0-5之间自动恢复，大于5之后不恢复。）
     * start(0, 0, 5, 1000, 1)
     * //HP恢复（玩家当前Hp为100，在0-100之间自动恢复）
     * start(100, 0, 100, 1000, 1)
     */
    public start(current: number, from: number, to: number, cd: number, unit: number = 1): Recover {
        if (this._running) {
            throw new Error("Recover被重复开启");
        }
        this._value = 0;
        this._nextRecoverTime = (this._offlineAble ? app.storage.getUValue(this._name, 0) : 0);
        this._config = { from, to, cd, unit };
        this._running = true;
        this.adjustValue(current);
        return this;
    }

    /**
     * 注册下一次恢复回调
     * @returns 
     */
    private _registerNextRecover(): void {
        if (!this.inRange) {
            return;
        }
        if (G_Data.server.time >= this._nextRecoverTime) {
            this._tick(Math.floor((G_Data.server.time - this._nextRecoverTime) / this._config.cd));
            return;
        }
        cc.director.getScheduler().enableForTarget(this);
        cc.director.getScheduler().unscheduleAllForTarget(this);
        cc.director.getScheduler().schedule(() => {
            this._tick(1);
        }, this, (this._nextRecoverTime - G_Data.server.time) / 1000);
    }

    private get inRange(): boolean {
        return (this._config.to > this._config.from && this._value < this._config.to && this._value >= this._config.from) ||
            (this._config.to < this._config.from && this._value > this._config.to && this._value <= this._config.from)
    }

    /**
     * 暂停
     */
    public pause(): void {
        if (this._running) {
            this._running = false;
        }
    }

    /**
     * 恢复
     */
    public resume(): void {
        if (!this._running) {
            this._running = true;
        }
    }

    /**
     * 停止
     * @param offAllCall 是否清理所有updated监听
     */
    public stop(offAllCall: boolean = true): void {
        this._running = false;
        this._cancel = true;
        cc.director.getScheduler().unscheduleAllForTarget(this);
        if (offAllCall && this._onUpdateListener) {
            this._onUpdateListener.clear();
        }
    }

    /**
     * 添加数据更新监听
     * @param call 监听函数
     * @param caller 可选参数,监听者
     */
    public onUpdated(call: (value: number, addValue: number, name?: string) => void, caller?: any): void {
        this._onUpdateListener = this._onUpdateListener || new Map();
        this._onUpdateListener.set(call, caller);
    }

    /**
     * 移除数据更新监听
     * @param call 监听函数
     * @returns 
     */
    public offUpdated(call: (value: number, addValue: number) => void): boolean {
        return this._onUpdateListener && this._onUpdateListener.delete(call);
    }

    private _tick(rate: number = 1): void {
        if (!this._running) {
            return;
        }
        this._nextRecoverTime = 0;
        this.adjustValue(this._config.unit * rate);
    }
}